package me.illtamer.rpc.transport.socket.client;

import lombok.extern.slf4j.Slf4j;
import me.illtamer.rpc.entity.RpcRequest;
import me.illtamer.rpc.entity.RpcResponse;
import me.illtamer.rpc.enumeration.ResponseCode;
import me.illtamer.rpc.enumeration.RpcError;
import me.illtamer.rpc.exception.RpcException;
import me.illtamer.rpc.loadbalancer.LoadBalancer;
import me.illtamer.rpc.loadbalancer.RandomLoadBalancer;
import me.illtamer.rpc.register.NacosServiceDiscovery;
import me.illtamer.rpc.register.ServiceDiscovery;
import me.illtamer.rpc.transport.RpcClient;
import me.illtamer.rpc.transport.socket.util.ObjectReader;
import me.illtamer.rpc.transport.socket.util.ObjectWriter;
import me.illtamer.serializer.CommonSerializer;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;

/**
 * Socket 方式进行远程调用处理的客户端
 * */
@Slf4j
public class SocketClient implements RpcClient {

    private final ServiceDiscovery serviceDiscovery;
    private final CommonSerializer serializer;

    public SocketClient() {
        this(DEFAULT_SERIALIZER, new RandomLoadBalancer());
    }

    public SocketClient(int serializerCode) {
        this(serializerCode, new RandomLoadBalancer());
    }

    public SocketClient(LoadBalancer loadBalancer) {
        this(DEFAULT_SERIALIZER, loadBalancer);
    }

    public SocketClient(int serializerCode, LoadBalancer loadBalancer) {
        this.serviceDiscovery = new NacosServiceDiscovery(loadBalancer);
        this.serializer = CommonSerializer.getByCode(serializerCode);
    }

    /**
     * socket 套接字实现 TCP 网络传输
     * @return 返回 RPC 结果对象 {@link me.illtamer.rpc.entity.RpcResponse}
     */
    @Override
    public Object sendRequest(RpcRequest rpcRequest) {
        if (serializer == null) {
            log.error("未设置序列化器");
            throw new RpcException(RpcError.SERVICE_NOT_FOUND);
        }
        InetSocketAddress address = serviceDiscovery.lookupService(rpcRequest.getInterfaceName());
        try (
                Socket socket = new Socket();
        ) {
            socket.connect(address);
            try (
                    OutputStream output = socket.getOutputStream();
                    InputStream input = socket.getInputStream();
            ) {
                ObjectWriter.writeObject(output, rpcRequest, serializer);
                Object object = ObjectReader.readObject(input);
                RpcResponse<?> rpcResponse = (RpcResponse<?>) object;
                if (rpcResponse == null) {
                    log.error("服务调用失败, service: {}", rpcRequest.getInterfaceName());
                    throw new RpcException(RpcError.SERVICE_INVOCATION_FAILURE, "service: " + rpcRequest.getInterfaceName());
                }
                if (rpcResponse.getStatusCode() != ResponseCode.SUCCESS.getCode()) {
                    log.error("服务调用失败, service: {} response: {}", rpcRequest.getInterfaceName(), rpcResponse);
                    throw new RpcException(RpcError.SERVICE_INVOCATION_FAILURE, "service: " + rpcRequest.getInterfaceName());
                }
                return rpcResponse;
            }
        } catch (IOException e) {
            log.error("方法调用出现错误：", e);
            throw new RpcException("服务调用失败: ", e);
        }
    }

}
